package com.notifier.event;

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.filter.ThresholdFilter;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.IThrowableProxy;
import ch.qos.logback.classic.spi.StackTraceElementProxy;
import ch.qos.logback.classic.spi.ThrowableProxy;
import ch.qos.logback.core.UnsynchronizedAppenderBase;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.time.LocalDateTime;

/**
 * 错误日志拦截器
 * 职责：拦截 ERROR 日志，转换为 ErrorEvent 并发布
 *
 * @author Amadeus
 * @date 2025/10/14
 */
@Slf4j
@Component
public class SendErrorMsgAppender extends UnsynchronizedAppenderBase<ILoggingEvent> {

    @Autowired
    private ErrorEventPublisher errorEventPublisher;

    @Value("${spring.application.name}")
    private String applicationName;

    @Value("${spring.profiles.active}")
    private String activeProfile;

    @PostConstruct
    public void init() {
        LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
        ThresholdFilter filter = new ThresholdFilter();
        filter.setLevel("ERROR");
        filter.setContext(context);
        filter.start();
        this.addFilter(filter);
        this.setContext(context);
        context.getLogger("ROOT").addAppender(SendErrorMsgAppender.this);
        super.start();
        log.info("错误日志拦截器初始化完成，应用：{}", applicationName);
    }

    @Override
    protected void append(ILoggingEvent event) {
        try {
            // 转换为错误事件
            ErrorEvent errorEvent = convertToErrorEvent(event);

            // 发布事件（异步处理）
            errorEventPublisher.publish(errorEvent);
        } catch (Exception e) {
            // 避免通知逻辑影响业务
            log.warn("处理错误事件失败", e);
        }
    }

    /**
     * 将 Logback 事件转换为错误事件
     */
    private ErrorEvent convertToErrorEvent(ILoggingEvent event) {
        IThrowableProxy iThrowableProxy = event.getThrowableProxy();

        // 构建完整错误消息（用于发送）
        StringBuilder fullErrorMessage = new StringBuilder();
        fullErrorMessage.append("服务:").append(applicationName).append("\n");
        fullErrorMessage.append("环境:").append(activeProfile).append("\n");

        // 错误类型和消息
        String errorType = null;
        String errorMessage = null;
        String location = null;
        StringBuilder stackTrace = new StringBuilder();

        if (iThrowableProxy instanceof ThrowableProxy) {
            ThrowableProxy throwableProxy = (ThrowableProxy) iThrowableProxy;
            Throwable throwable = throwableProxy.getThrowable();

            errorType = throwable.getClass().getName();
            errorMessage = throwable.getMessage();

            // 添加格式化消息
            fullErrorMessage.append(event.getFormattedMessage()).append("\n");

            // 添加异常信息
            String message = throwable.toString();
            fullErrorMessage.append(message).append("\n");
            if (StrUtil.isNotEmpty(errorMessage)) {
                fullErrorMessage.append(errorMessage).append("\n");
            }

            // 添加堆栈信息
            StackTraceElementProxy[] stackTraceElementProxy = iThrowableProxy.getStackTraceElementProxyArray();
            for (StackTraceElementProxy proxy : stackTraceElementProxy) {
                String steString = proxy.getSTEAsString();
                fullErrorMessage.append(steString).append("\n");
                stackTrace.append(steString).append("\n");
            }

            // 获取第一个属于我们项目的堆栈位置
            if (stackTraceElementProxy.length > 0) {
                for (StackTraceElementProxy proxy : stackTraceElementProxy) {
                    StackTraceElement element = proxy.getStackTraceElement();
                    String className = element.getClassName();
                    if (className.startsWith("highle.")) {
                        location = className + "." + element.getMethodName() + ":" + element.getLineNumber();
                        break;
                    }
                }
                // 如果没找到项目代码，使用第一行
                if (location == null) {
                    StackTraceElement element = stackTraceElementProxy[0].getStackTraceElement();
                    location = element.getClassName() + "." + element.getMethodName() + ":" + element.getLineNumber();
                }
            }
        } else {
            // 没有异常堆栈，只有日志消息
            fullErrorMessage.append(event.getFormattedMessage());
            errorMessage = event.getFormattedMessage();
        }

        // 生成错误签名（用于去重）
        String signature = generateErrorSignature(errorType, location, errorMessage);

        // 构建错误事件
        return ErrorEvent.builder()
                .applicationName(applicationName)
                .environment(activeProfile)
                .signature(signature)
                .errorType(errorType)
                .errorMessage(errorMessage)
                .stackTrace(stackTrace.toString())
                .location(location)
                .occurTime(LocalDateTime.now())
                .loggerName(event.getLoggerName())
                .formattedMessage(event.getFormattedMessage())
                .fullErrorMessage(fullErrorMessage.toString())
                .build();
    }

    /**
     * 生成错误签名（用于去重）
     * 组成：异常类型 + 发生位置 + 标准化的错误消息
     */
    private String generateErrorSignature(String errorType, String location, String errorMessage) {
        StringBuilder signature = new StringBuilder();

        if (errorType != null) {
            signature.append(errorType).append("::");
        }

        if (location != null) {
            signature.append(location).append("::");
        }

        if (errorMessage != null) {
            // 标准化错误消息，移除动态数据
            String normalizedMessage = normalizeErrorMessage(errorMessage);
            signature.append(normalizedMessage);
        }

        return signature.toString();
    }

    /**
     * 标准化错误消息，移除动态变化的数据
     */
    private String normalizeErrorMessage(String message) {
        if (message == null) {
            return "";
        }

        // 限制长度
        if (message.length() > 300) {
            message = message.substring(0, 300);
        }

        return message
                // 移除6位以上数字（订单号、用户ID等）
                .replaceAll("\\d{6,}", "[NUM]")
                // 移除UUID
                .replaceAll("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}", "[UUID]")
                // 移除IP地址
                .replaceAll("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}", "[IP]")
                // 移除时间戳（毫秒）
                .replaceAll("\\d{13}", "[TIMESTAMP]")
                // 移除日期时间格式
                .replaceAll("\\d{4}-\\d{2}-\\d{2}\\s+\\d{2}:\\d{2}:\\d{2}", "[DATETIME]")
                // 移除16进制地址
                .replaceAll("@[0-9a-f]{6,}", "[@ADDR]")
                // 多个空格合并为一个
                .replaceAll("\\s+", " ")
                .trim();
    }
}
